﻿using System;
using System.Data;
using System.Linq;
using SD.LLBLGen.Pro.QuerySpec;
using SD.LLBLGen.Pro.QuerySpec.Adapter;
using VNet.VisiSuite.BusinessLogic.BusinessException;
using VNet.VisiSuite.Common.Resource;
using VNet.VisiSuite.Dal.DatabaseSpecific;
using VNet.VisiSuite.Dal.EntityClasses;
using VNet.VisiSuite.Dal.FactoryClasses;
using VNet.VisiSuite.Dal.HelperClasses;
using VNet.VisiSuite.Service.Contact.Message.Item;
using VNet.VisiSuite.Service.Contact.Message.Base;
using System.Collections.Generic;
using VNet.VisiSuite.Common.Logger;

namespace VNet.VisiSuite.BusinessLogic
{
    public class ProductUtil
    {
        private readonly QueryFactory _queryFactory = new QueryFactory();

        public GetDataResponse<IList<ProductItem>> GetProducts(ServiceBaseRequest request)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    EntityQuery<ProductEntity> query =
                        _queryFactory.Product.WithPath(ProductEntity.PrefetchPathUserLogin,
                            ProductEntity.PrefetchPathUserLogin1);

                    var entities = (EntityCollection<ProductEntity>) adapter.FetchQuery(query);
                    if (entities == null)
                        return new GetDataResponse<IList<ProductItem>>
                        {
                            Success = false,
                            Error = BusinessLogicError.GetProductsFailed
                        };

                    var result = new List<ProductItem>();
                    foreach (var entity in entities)
                    {
                        var productItem = new ProductItem
                        {
                            ProductId = entity.ProductId,
                            Sku = entity.Sku,
                            Description = entity.Description,
                            Revision = entity.Revision,
                            Enabled = entity.Enabled,
                            CreatedDate = entity.CreatedDate,
                            LastUpdatedDate = entity.LastUpdatedDate,
                            CreatedByUser =
                                string.Format("{0} {1}", entity.UserLogin.FirstName, entity.UserLogin.LastName),
                            LastUpdatedByUser =
                                string.Format("{0} {1}", entity.UserLogin1.FirstName, entity.UserLogin1.LastName),
                        };
                        result.Add(productItem);
                    }

                    return new GetDataResponse<IList<ProductItem>> {Success = true, Data = result,};
                }
                catch (Exception ex)
                {
                    FileLog.Error("GetProducts", ex);
                    return new GetDataResponse<IList<ProductItem>>
                    {
                        Success = false,
                        Error = BusinessLogicError.GetProductsFailed
                    };
                }
            }
        }

        public SaveDataResponse<ProductItem> InsertProduct(SaveDataRequest<ProductItem> request)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    if (!string.IsNullOrEmpty(CheckProductNameUnique(adapter, request.Data)))
                        return new SaveDataResponse<ProductItem>
                        {
                            Error = BusinessLogicError.ProductNameExisted,
                            Success = false
                        };

                    DateTime dateTimeNow = DateTime.UtcNow;
                    var productEntity = new ProductEntity
                    {
                        Sku = request.Data.Sku,
                        Description = request.Data.Description,
                        Revision = request.Data.Revision,
                        Enabled = request.Data.Enabled,
                        LastUpdatedBy = request.Identity.UserLoginId,
                        LastUpdatedDate = dateTimeNow,
                        CreatedBy = request.Identity.UserLoginId,
                        CreatedDate = dateTimeNow,
                    };

                    bool result = adapter.SaveEntity(productEntity, true);
                    if (!result)
                        return new SaveDataResponse<ProductItem>
                        {
                            Success = false,
                            Error = BusinessLogicError.InsertProductFailed
                        };

                    // Get product entity after updated

                    var q = _queryFactory.Create()
                        .Select(() => new
                        {
                            Sku = ProductFields.Sku.Source("c").ToValue<string>(),
                            Description = ProductFields.Description.Source("c").ToValue<string>(),
                            Revision = ProductFields.Revision.Source("c").ToValue<string>(),
                            Enabled = ProductFields.Enabled.Source("c").ToValue<Boolean>(),
                            LastUpdatedDate = ProductFields.LastUpdatedDate.Source("c").ToValue<DateTime>(),
                            CreatedDate = ProductFields.CreatedDate.Source("c").ToValue<DateTime>(),
                            ProductId = ProductFields.ProductId.Source("c").ToValue<int>(),
                            FirstName1 =
                                (UserLoginFields.FirstName.Source("u").SetFieldAlias("FistName1")).ToValue<string>(),
                            LastName1 =
                                (UserLoginFields.LastName.Source("u").SetFieldAlias("LastName1")).ToValue<string>(),
                        })
                        .From(
                            _queryFactory.Product.As("c")
                                .InnerJoin(ProductEntity.Relations.UserLoginEntityUsingLastUpdatedBy, "c", "u"))
                        .Where(ProductFields.Sku.Source("c") == productEntity.Sku);

                    var ret = adapter.FetchQuery(q);
                    if (ret.Any())
                    {
                        string user = string.Format(FormatString.NameFormat, ret[0].FirstName1, ret[0].LastName1);
                        return new SaveDataResponse<ProductItem>
                        {
                            Success = true,
                            Data = new ProductItem
                            {
                                ProductId = ret[0].ProductId,
                                Sku = ret[0].Sku,
                                Description = ret[0].Description,
                                Revision = ret[0].Revision,
                                Enabled = ret[0].Enabled,
                                LastUpdatedDate = ret[0].LastUpdatedDate,
                                CreatedDate = ret[0].CreatedDate,
                                LastUpdatedByUser = user,
                                CreatedByUser = user,
                            }
                        };
                    }
                    return new SaveDataResponse<ProductItem>
                    {
                        Success = false,
                        Error = BusinessLogicError.InsertProductFailed
                    };
                }
                catch (Exception ex)
                {
                    FileLog.Error("InsertProduct", ex);
                    return new SaveDataResponse<ProductItem>
                    {
                        Success = false,
                        Error = BusinessLogicError.InsertProductFailed
                    };
                }
            }
        }

        public SaveDataResponse<ProductItem> UpdateProduct(SaveDataRequest<ProductItem> request)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    EntityQuery<ProductEntity> query =
                        _queryFactory.Product.Where(ProductFields.ProductId == request.Data.ProductId);

                    ProductEntity product = adapter.FetchFirst(query);
                    if (product == null)
                        return new SaveDataResponse<ProductItem>
                        {
                            Error = BusinessLogicError.ProductDoesNotExist,
                            Success = false
                        };
                    var message = CheckProductNameUnique(adapter, request.Data);
                    if (!string.IsNullOrEmpty(message))
                    {
                        return new SaveDataResponse<ProductItem>
                        {
                            Error = BusinessLogicError.ProductNameExisted,
                            Success = false
                        };
                    }
                    product.Sku = request.Data.Sku;
                    product.Description = request.Data.Description;
                    product.Revision = request.Data.Revision;
                    product.Enabled = request.Data.Enabled;
                    product.LastUpdatedBy = request.Identity.UserLoginId;
                    product.LastUpdatedDate = DateTime.UtcNow;

                    var result = adapter.SaveEntity(product, true);

                    if (!result)
                        return new SaveDataResponse<ProductItem>
                        {
                            Success = false,
                            Error = BusinessLogicError.UpdateProductFailed
                        };

                    // Get product entity after updated
                    var q = _queryFactory.Create()
                        .Select(() => new
                        {
                            Sku = ProductFields.Sku.Source("c").ToValue<string>(),
                            Description = ProductFields.Description.Source("c").ToValue<string>(),
                            Revision = ProductFields.Revision.Source("c").ToValue<string>(),
                            Enabled = ProductFields.Enabled.Source("c").ToValue<Boolean>(),
                            LastUpdatedDate = ProductFields.LastUpdatedDate.Source("c").ToValue<DateTime>(),
                            CreatedDate = ProductFields.CreatedDate.Source("c").ToValue<DateTime>(),
                            ProductId = ProductFields.ProductId.Source("c").ToValue<int>(),
                            FirstName1 =
                                (UserLoginFields.FirstName.Source("u").SetFieldAlias("FistName1")).ToValue<string>(),
                            LastName1 =
                                (UserLoginFields.LastName.Source("u").SetFieldAlias("LastName1")).ToValue<string>(),
                        })
                        .From(
                            _queryFactory.Product.As("c")
                                .InnerJoin(ProductEntity.Relations.UserLoginEntityUsingLastUpdatedBy, "c", "u"))
                        .Where(ProductFields.Sku.Source("c") == product.Sku);

                    var val = adapter.FetchQuery(q).FirstOrDefault();
                    if (val == null)
                        return new SaveDataResponse<ProductItem>
                        {
                            Success = false,
                            Error = BusinessLogicError.UpdateProductFailed
                        };

                    return new SaveDataResponse<ProductItem>
                    {
                        Success = true,
                        Data = new ProductItem
                        {
                            ProductId = val.ProductId,
                            Sku = val.Sku,
                            Description = val.Description,
                            Revision = val.Revision,
                            Enabled = val.Enabled,
                            CreatedDate = val.CreatedDate,
                            LastUpdatedByUser = string.Format(FormatString.NameFormat, val.FirstName1, val.LastName1),
                            LastUpdatedDate = val.LastUpdatedDate,
                        }
                    };
                }
                catch (Exception ex)
                {
                    FileLog.Error("UpdateProduct", ex);
                    return new SaveDataResponse<ProductItem>
                    {
                        Success = false,
                        Error = BusinessLogicError.UpdateProductFailed
                    };
                }
            }
        }

        public SaveDataResponse<IList<ProductItem>> ImportProducts(SaveDataRequest<IList<ProductItem>> request)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    adapter.StartTransaction(IsolationLevel.ReadCommitted, "ImportProducts");
                    var items = request.Data;
                    EntityQuery<ProductEntity> query =
                        _queryFactory.Product.WithPath(ProductEntity.PrefetchPathUserLogin,
                            ProductEntity.PrefetchPathUserLogin1);

                    var entities = (EntityCollection<ProductEntity>) adapter.FetchQuery(query);
                    foreach (var modelItem in items)
                    {
                        //Update Product
                        if (entities.Any(x => x.Sku == modelItem.Sku))
                        {
                            var product = entities.FirstOrDefault(x => x.Sku == modelItem.Sku);
                            if (product != null)
                            {
                                product.Description = modelItem.Description;
                                product.Revision = modelItem.Revision;
                                product.Enabled = modelItem.Enabled;
                                product.LastUpdatedDate = DateTime.UtcNow;
                                product.LastUpdatedBy = request.Identity.UserLoginId;
                            }
                        }
                        else
                        {
                            var newProduct = new ProductEntity
                            {
                                Sku = modelItem.Sku,
                                Description = modelItem.Description,
                                Revision = modelItem.Revision,
                                Enabled = modelItem.Enabled,
                                LastUpdatedDate = DateTime.UtcNow,
                                LastUpdatedBy = request.Identity.UserLoginId,
                                CreatedDate = DateTime.UtcNow,
                                CreatedBy = request.Identity.UserLoginId,
                            };
                            entities.Add(newProduct);
                        }
                    }
                    adapter.SaveEntityCollection(entities, true, true);
                    adapter.Commit();
                    return new SaveDataResponse<IList<ProductItem>>
                    {
                        Data = null,
                        Success = true
                    };
                }
                catch (Exception ex)
                {
                    adapter.Rollback();
                    FileLog.Error("ImportProducts", ex);
                    return new SaveDataResponse<IList<ProductItem>>
                    {
                        Data = null,
                        Error = BusinessLogicError.ImportProductsFailed,
                        Success = false
                    };
                }
            }
        }

        private string CheckProductNameUnique(DataAccessAdapter adapter, ProductItem data)
        {
            var query =
                _queryFactory.Product.Where(ProductFields.Sku == data.Sku & ProductFields.ProductId != data.ProductId);
            var products = (EntityCollection<ProductEntity>) adapter.FetchQuery(query);

            if (products != null && products.Count > 0)
                return ErrorMessages.ExistedProductName;
            return null;
        }

        public DeleteDataResponse DeleteProduct(DeleteDataRequest request)
        {
            using (var adapter = new DataAccessAdapter())
            {
                try
                {
                    var product_plan_query =
                        _queryFactory.ProductionPlan.Where(ProductionPlanFields.ProductId == request.DataId)
                            .Include(ProductionPlanFields.ProductId)
                            .Select(ProductionPlanFields.ProductId);
                    var product_plan = adapter.FetchQuery(product_plan_query);

                    if (product_plan != null && product_plan.Count > 0)
                        return new DeleteDataResponse
                        {
                            Success = false,
                            Error = new ServiceError {Description = ErrorMessages.ProductInUsed}
                        };

                    var query2 = _queryFactory.Product.Where(ProductFields.ProductId == request.DataId);
                    var product_entity = adapter.FetchFirst(query2);
                    if (product_entity == null)
                        return new DeleteDataResponse {Error = BusinessLogicError.ProductDoesNotExist, Success = false};

                    if (!adapter.DeleteEntity(product_entity))
                        return new DeleteDataResponse {Error = BusinessLogicError.DeleteProductFailed, Success = false};

                    return new DeleteDataResponse {Success = true, DataId = request.DataId};
                }
                catch (Exception exp)
                {
                    return new DeleteDataResponse {Success = false, Error = BusinessLogicError.DeleteProductFailed};
                }
            }
        }

        
    }
}
